`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/19 21:11:13
// Design Name: 
// Module Name: syn_fifo_fwft
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

(* dont_touch="true" *)

module syn_fifo_fwft #(
    parameter                   FDW         =   10,     //fifo data width
    parameter                   FDPW        =   6,      //fifo depth width, the depth of the fifo is 2^FDPW
    parameter                   HT          =   100,    //high threshold, larger than this value 1'b1,prog_full threshold
    parameter                   LT          =   3       //low threshold, less than this value 1'b1, prog_empty threshold
    )(
    //system signal
    input                       clk,
    input                       rst_n,
    
    //write side
    input                       wr_en,
    input       [FDW-1:0]       din,
    output  reg                 full,
    output  reg                 prog_full,
    
    //read side
    input                       rd_en,
    output      [FDW-1:0]       dout,
    output reg  [FDPW:0]        fifo_count,
    output                      empty,
    output reg                  prog_empty
    );

    //------------Declare Signals----------------
    reg         [FDPW:0]        fifo_cnt;
    reg         [FDPW-1:0]      waddr;
    reg         [FDPW-1:0]      raddr;
    reg         [FDPW-1:0]      raddr_inc1;
    wire        [FDPW-1:0]      raddr_final;
   
    wire                        fifo_cnt_inc;  //counter inc valid
    wire                        fifo_cnt_dec;  //counter dec valid
    wire                        fifo_wr_valid;
    wire                        fifo_rd_valid;
    reg                         fifo_wr_valid_r;

    //---------------Processing------------------
    //fifo wr/rd
    assign  fifo_wr_valid       =   (wr_en & (~full));
    assign  fifo_rd_valid       =   (rd_en & (~empty));

    always@(negedge rst_n or posedge clk)begin
        if(!rst_n)begin
            fifo_wr_valid_r     <=  1'b0;
        end
        else begin
            fifo_wr_valid_r     <=  fifo_wr_valid;
        end
    end

    //fifo cnt
    assign  fifo_cnt_inc        =   fifo_wr_valid_r & (~fifo_rd_valid);     //write valid and read invalid
    assign  fifo_cnt_dec        =   (~fifo_wr_valid_r) & fifo_rd_valid;     //read valid and write invalid

    always@(negedge rst_n or posedge clk)begin
        if(!rst_n)begin
            fifo_cnt            <=  {(FDPW+1){1'b0}};
        end
        else if(fifo_cnt_inc)begin
            fifo_cnt            <=  fifo_cnt + 1;
        end
        else if(fifo_cnt_dec)begin
            fifo_cnt            <=  fifo_cnt - 1;
        end
    end
       
    //address
    always@(negedge rst_n or posedge clk)begin
        if(!rst_n)begin
            waddr               <=  {FDPW{1'b0}};
        end
        else if(fifo_wr_valid)begin
            waddr               <=  waddr + 1;
        end
    end

    always@(negedge rst_n or posedge clk)begin
        if(!rst_n)begin
            raddr               <=  {FDPW{1'b0}};
        end
        else if(fifo_rd_valid)begin
            raddr               <=  raddr+1;
        end
    end

    always@(negedge rst_n or posedge clk)begin
        if(!rst_n)begin
            raddr_inc1          <=  {{(FDPW-1){1'b0}},1'b1};
        end
        else if(fifo_rd_valid)begin
            raddr_inc1          <=  raddr_inc1 + 1;
        end
    end

    assign raddr_final          =   fifo_rd_valid?raddr_inc1:raddr;
       
    //status signals
    always@(negedge rst_n or posedge clk)begin
        if(!rst_n)begin
            full                <=  1'b1;
            prog_full           <=  1'b1;
            prog_empty          <=  1'b1;
            fifo_count          <=  {FDPW{1'b0}};
        end
        else begin
            full                <=  ~(fifo_cnt < {1'b1,{(FDPW){1'b0}}});
            prog_full           <=  (fifo_cnt > HT);
            prog_empty          <=  (fifo_cnt < LT);
            fifo_count          <=  fifo_cnt;
        end
    end

    assign  empty   =   fifo_cnt == {FDPW{1'b0}};

    //fifo mem
    simple_dual_ram #(
        .AW                     (FDPW),
        .DW                     (FDW)
    ) U_simple_dual_ram(
        .clk                    (clk),

        //side a 
        .wea                    (fifo_wr_valid),
        .ena                    (1'b1),
        .dia                    (din),
        .addra                  (waddr),

        //side b
        .enb                    (1'b1),
        .addrb                  (raddr_final),
        .dob                    (dout)
    );

endmodule

module simple_dual_ram #(
    parameter                   AW  = 10,
                                DW  = 16
    )(
    input                       clk,

    //side a 
    input                       wea,
    input                       ena,
    input       [DW-1:0]        dia,
    input       [AW-1:0]        addra,

    //side b
    input                       enb,
    input       [AW-1:0]        addrb,
    output reg  [DW-1:0]        dob
    );

    localparam                  RAM_DEP = 2**AW;

    reg         [DW-1:0]        ram     [RAM_DEP-1:0];

    //'initial' will be ignored at implementation
    integer i;
    initial for (i=0; i<RAM_DEP; i=i+1) ram[i] = 0;

    //wr
    always @(posedge clk) begin 
         if (ena) begin
            if (wea) begin
                ram[addra]   <=  dia;
            end
         end
    end

    //rd
    always @(posedge clk) begin 
         if (enb)
            dob             <=  ram[addrb];
    end

endmodule